#pragma once

#include "pch.h"

struct Field
{
	std::string m_Name;

	enum class BasicType {
		Integer,
		Float,
		Decimal,
		String,
		Binary,
		DateTime,
		Boolean,
		Json,
		Unknown
	};

	enum class Type {
		TinyInt,
		SmallInt,
		MediumInt,
		Int,
		BigInt,

		Float,
		Double,

		Decimal,

		Boolean,

		Char,
		VarChar,
		TinyText,
		Text,
		MediumText,
		LongText,

		Binary,
		VarBinary,
		TinyBlob,
		Blob,
		MediumBlob,
		LongBlob,

		Date,
		Time,
		DateTime,
		Timestamp,
		Year,

		Json,

		Unknown
	};
	static BasicType GetBasicType(Type type) {
		switch (type) {
		case Type::TinyInt:
		case Type::SmallInt:
		case Type::MediumInt:
		case Type::Int:
		case Type::BigInt:
			return BasicType::Integer;
		case Type::Float:
		case Type::Double:
			return BasicType::Float;

		case Type::Decimal:
			return BasicType::Decimal;

		case Type::Boolean:
			return BasicType::Boolean;

		case Type::Char:
		case Type::VarChar:
		case Type::TinyText:
		case Type::Text:
		case Type::MediumText:
		case Type::LongText:
			return BasicType::String;

		case Type::Binary:
		case Type::VarBinary:
		case Type::TinyBlob:
		case Type::Blob:
		case Type::MediumBlob:
		case Type::LongBlob:
			return BasicType::Binary;
		case Type::Date:
		case Type::Time:
		case Type::DateTime:
		case Type::Timestamp:
		case Type::Year:
			return BasicType::DateTime;
		case Type::Json:
			return BasicType::Json;

		default:
			return BasicType::Unknown;
		}
	}
	BasicType m_BasicType{ BasicType::Unknown };
	Type m_Type{ Type::Unknown };

	void SetType(const Type& type)
	{
		m_Type = type;
		m_BasicType = GetBasicType(type);
	}

	bool SetDefault(const char* defV)
	{
		if (defV)
			m_Default = defV;

		return defV;
	}

	void SetDefaultValue(const std::string& defV)
	{
		std::string v = "DEFAULT ";
		v += defV;
		SetDefault(v.c_str());

	}
	void SetDefaultFloat(const float& defV)
	{
		std::string v = "DEFAULT ";
		v += Float2String(defV);
		SetDefault(v.c_str());

	}
	void SetDefaultInt(const int& defV)
	{
		std::string v = "DEFAULT ";
		v += Int2String(defV);
		SetDefault(v.c_str());
	}
	static std::string Float2String(const float& value)
	{
		return std::to_string(value);
	}
	static std::string Int2String(const int& value)
	{
		return std::to_string(value);
	}

	void SetDecimal(int m, int d)
	{
		SetType(Type::Decimal);
		m_Decimal_M = m;
		m_Decimal_D = d;
	}
	void SetChar(int dataLen)
	{
		SetType(Type::Char);
		m_DataLen = dataLen;
	}
	void SetVarchar(int dataLen)
	{
		SetType(Type::VarChar);
		m_DataLen = dataLen;
	}

	void SetName(const std::string& name)
	{
		m_Name = name;
	}
	bool Availed() const
	{
		if (m_Type == Type::Unknown)
		{
			return false;
		}
		if (m_BasicType == BasicType::Unknown)
		{
			return false;
		}
		if (m_Type == Type::Decimal)
		{
			if (m_Decimal_M < m_Decimal_D)
			{
				return false;
			}
		}
		if (m_Type == Type::VarChar || m_Type == Type::Char)
		{
			if (m_DataLen <= 0)
			{
				return false;
			}

		}
		return true;
	}
	std::string GetTypeText() const
	{
		using namespace std;

		switch (m_Type)
		{
		case Field::Type::TinyInt:     return "TINYINT";
		case Field::Type::SmallInt:    return "SMALLINT";
		case Field::Type::MediumInt:   return "MEDIUMINT";
		case Field::Type::Int:         return "INT";
		case Field::Type::BigInt:      return "BIGINT";

		case Field::Type::Float:       return "FLOAT";
		case Field::Type::Double:      return "DOUBLE";
		case Field::Type::Decimal:
			return "DECIMAL(" + to_string(m_Decimal_M) + "," + to_string(m_Decimal_D) + ")";

		case Field::Type::Boolean:     return "BOOLEAN";

		case Field::Type::Char:
			return "CHAR(" + to_string(m_DataLen) + ")";
		case Field::Type::VarChar:
			return "VARCHAR(" + to_string(m_DataLen) + ")";
		case Field::Type::TinyText:    return "TINYTEXT";
		case Field::Type::Text:        return "TEXT";
		case Field::Type::MediumText:  return "MEDIUMTEXT";
		case Field::Type::LongText:    return "LONGTEXT";

		case Field::Type::Binary:
			return "BINARY(" + to_string(m_DataLen) + ")";
		case Field::Type::VarBinary:
			return "VARBINARY(" + to_string(m_DataLen) + ")";
		case Field::Type::TinyBlob:    return "TINYBLOB";
		case Field::Type::Blob:        return "BLOB";
		case Field::Type::MediumBlob:  return "MEDIUMBLOB";
		case Field::Type::LongBlob:    return "LONGBLOB";

		case Field::Type::Date:        return "DATE";
		case Field::Type::Time:        return "TIME";
		case Field::Type::DateTime:    return "DATETIME";
		case Field::Type::Timestamp:   return "TIMESTAMP";
		case Field::Type::Year:        return "YEAR";

		case Field::Type::Json:        return "JSON";

		case Field::Type::Unknown:
		default:
			return "UNKNOWN";
		}
	}

public:
	int m_DataLen{ 11 };
	bool m_AutoInc{ false };
	bool m_PrimaryKey{ false };
	bool m_NotNull{ false };
	int m_Decimal_M{ 2 };
	int m_Decimal_D{ 2 };

	std::string m_Default{};

	// Cache Variable
	//std::string m_ColumnType;
	//std::string m_IsNullString;
	//std::string m_ColumnKey;
	//std::string m_Extra;

	void SetColumType(const std::string& columnType);

	bool SetColumKey(const std::string& columnKey)
	{
		if (columnKey == "PRI")
		{
			m_PrimaryKey = true;
			return true;
		}
		return false;
	}
	bool SetIsNullString(const std::string& isNullString)
	{
		if (isNullString == "YES" || isNullString == "yes")
		{
			m_NotNull = false;
			return true;
		}
		if (isNullString == "NO" || isNullString == "no")
		{
			m_NotNull = true;
			return true;
		}
		return false;
	}
	bool SetExtra(const std::string& extra)
	{
		if (extra == "auto_increment")
		{
			m_AutoInc = true;
			return true;
		}

		return false;
	}
	bool SetDefaultSource(const char* defV)
	{
		if (!defV)
		{
			return false;
		}
		if (std::string(defV) == "curdate()")
		{
			return SetDefault("(CURRENT_DATE)");
		}
		return SetDefault(defV);
	}
};
